home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tcl / dist6.3 / tclCkalloc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-01-19  |  15.0 KB  |  530 lines

  1. /* 
  2.  * tclCkalloc.c --
  3.  *    Interface to malloc and free that provides support for debugging problems
  4.  *    involving overwritten, double freeing memory and loss of memory.
  5.  *
  6.  * Copyright 1991 Regents of the University of California
  7.  * Permission to use, copy, modify, and distribute this
  8.  * software and its documentation for any purpose and without
  9.  * fee is hereby granted, provided that the above copyright
  10.  * notice appear in all copies.  The University of California
  11.  * makes no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without
  13.  * express or implied warranty.
  14.  *
  15.  * This code contributed by Karl Lehenbauer and Mark Diekhans
  16.  *
  17.  */
  18.  
  19. #include "tclInt.h"
  20.  
  21. #define FALSE    0
  22. #define TRUE    1
  23.  
  24. #ifdef TCL_MEM_DEBUG
  25. #ifndef TCL_GENERIC_ONLY
  26. #include "tclUnix.h"
  27. #endif
  28.  
  29. #define GUARD_SIZE 8
  30.  
  31. struct mem_header {
  32.         long               length;
  33.         char              *file;
  34.         int                line;
  35.         struct mem_header *flink;
  36.         struct mem_header *blink;
  37.         unsigned char      low_guard[GUARD_SIZE];
  38.         char               body[1];
  39. };
  40.  
  41. static struct mem_header *allocHead = NULL;  /* List of allocated structures */
  42.  
  43. #define GUARD_VALUE  0341
  44.  
  45. /* static char high_guard[] = {0x89, 0xab, 0xcd, 0xef}; */
  46.  
  47. static int total_mallocs = 0;
  48. static int total_frees = 0;
  49. static int current_bytes_malloced = 0;
  50. static int maximum_bytes_malloced = 0;
  51. static int current_malloc_packets = 0;
  52. static int maximum_malloc_packets = 0;
  53. static int break_on_malloc = 0;
  54. static int trace_on_at_malloc = 0;
  55. static int  alloc_tracing = FALSE;
  56. static int  init_malloced_bodies = FALSE;
  57. #ifdef MEM_VALIDATE
  58.     static int  validate_memory = TRUE;
  59. #else
  60.     static int  validate_memory = FALSE;
  61. #endif
  62.  
  63.  
  64. /*
  65.  *----------------------------------------------------------------------
  66.  *
  67.  * dump_memory_info --
  68.  *     Display the global memory management statistics.
  69.  *
  70.  *----------------------------------------------------------------------
  71.  */
  72. static void
  73. dump_memory_info(outFile) 
  74.     FILE *outFile;
  75. {
  76.         fprintf(outFile,"total mallocs             %10d\n", 
  77.                 total_mallocs);
  78.         fprintf(outFile,"total frees               %10d\n", 
  79.                 total_frees);
  80.         fprintf(outFile,"current packets allocated %10d\n", 
  81.                 current_malloc_packets);
  82.         fprintf(outFile,"current bytes allocated   %10d\n", 
  83.                 current_bytes_malloced);
  84.         fprintf(outFile,"maximum packets allocated %10d\n", 
  85.                 maximum_malloc_packets);
  86.         fprintf(outFile,"maximum bytes allocated   %10d\n", 
  87.                 maximum_bytes_malloced);
  88. }
  89.  
  90. /*
  91.  *----------------------------------------------------------------------
  92.  *
  93.  * ValidateMemory --
  94.  *     Procedure to validate allocted memory guard zones.
  95.  *
  96.  *----------------------------------------------------------------------
  97.  */
  98. static void
  99. ValidateMemory (memHeaderP, file, line, nukeGuards)
  100.     struct mem_header *memHeaderP;
  101.     char              *file;
  102.     int                line;
  103.     int                nukeGuards;
  104. {
  105.     unsigned char *hiPtr;
  106.     int   idx;
  107.     int   guard_failed = FALSE;
  108.  
  109.     for (idx = 0; idx < GUARD_SIZE; idx++)
  110.         if (*(memHeaderP->low_guard + idx) != GUARD_VALUE) {
  111.             guard_failed = TRUE;
  112.             fflush (stdout);
  113.             fprintf(stderr, "low guard byte %d is 0x%x\n", idx, 
  114.                     *(memHeaderP->low_guard + idx) & 0xff);
  115.         }
  116.  
  117.     if (guard_failed) {
  118.         dump_memory_info (stderr);
  119.         fprintf (stderr, "low guard failed at %lx, %s %d\n",
  120.                  memHeaderP->body, file, line);
  121.         fflush (stderr);  /* In case name pointer is bad. */
  122.         fprintf (stderr, "Allocated at (%s %d)\n", memHeaderP->file,
  123.                  memHeaderP->line);
  124.         panic ("Memory validation failure");
  125.     }
  126.  
  127.     hiPtr = (unsigned char *)memHeaderP->body + memHeaderP->length;
  128.     for (idx = 0; idx < GUARD_SIZE; idx++)
  129.         if (*(hiPtr + idx) != GUARD_VALUE) {
  130.             guard_failed = TRUE;
  131.             fflush (stdout);
  132.             fprintf(stderr, "hi guard byte %d is 0x%x\n", idx, 
  133.                     *(hiPtr+idx) & 0xff);
  134.         }
  135.  
  136.     if (guard_failed) {
  137.         dump_memory_info (stderr);
  138.         fprintf (stderr, "high guard failed at %lx, %s %d\n",
  139.                  memHeaderP->body, file, line);
  140.         fflush (stderr);  /* In case name pointer is bad. */
  141.         fprintf (stderr, "Allocated at (%s %d)\n", memHeaderP->file,
  142.                  memHeaderP->line);
  143.         panic ("Memory validation failure");
  144.     }
  145.  
  146.     if (nukeGuards) {
  147.         memset ((char *) memHeaderP->low_guard, 0, GUARD_SIZE); 
  148.         memset ((char *) hiPtr, 0, GUARD_SIZE); 
  149.     }
  150.  
  151. }
  152.  
  153. /*
  154.  *----------------------------------------------------------------------
  155.  *
  156.  * Tcl_ValidateAllMemory --
  157.  *     Validates guard regions for all allocated memory.
  158.  *
  159.  *----------------------------------------------------------------------
  160.  */
  161. void
  162. Tcl_ValidateAllMemory (file, line)
  163.     char  *file;
  164.     int    line;
  165. {
  166.     struct mem_header *memScanP;
  167.  
  168.     for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink)
  169.         ValidateMemory (memScanP, file, line, FALSE);
  170.  
  171. }
  172.  
  173. /*
  174.  *----------------------------------------------------------------------
  175.  *
  176.  * Tcl_DumpActiveMemory --
  177.  *     Displays all allocated memory to stderr.
  178.  *
  179.  * Results:
  180.  *     Return TCL_ERROR if an error accessing the file occures, `errno' 
  181.  *     will have the file error number left in it.
  182.  *----------------------------------------------------------------------
  183.  */
  184. int
  185. Tcl_DumpActiveMemory (fileName)
  186.     char *fileName;
  187. {
  188.     FILE              *fileP;
  189.     struct mem_header *memScanP;
  190.     char              *address;
  191.  
  192.     fileP = fopen (fileName, "w");
  193.     if (fileP == NULL)
  194.         return TCL_ERROR;
  195.  
  196.     for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink) {
  197.         address = &memScanP->body [0];
  198.         fprintf (fileP, "%8lx - %8lx  %7d @ %s %d\n", address,
  199.                  address + memScanP->length - 1, memScanP->length,
  200.                  memScanP->file, memScanP->line);
  201.     }
  202.     fclose (fileP);
  203.     return TCL_OK;
  204. }
  205.  
  206. /*
  207.  *----------------------------------------------------------------------
  208.  *
  209.  * Tcl_DbCkalloc - debugging ckalloc
  210.  *
  211.  *        Allocate the requested amount of space plus some extra for
  212.  *        guard bands at both ends of the request, plus a size, panicing 
  213.  *        if there isn't enough space, then write in the guard bands
  214.  *        and return the address of the space in the middle that the
  215.  *        user asked for.
  216.  *
  217.  *        The second and third arguments are file and line, these contain
  218.  *        the filename and line number corresponding to the caller.
  219.  *        These are sent by the ckalloc macro; it uses the preprocessor
  220.  *        autodefines __FILE__ and __LINE__.
  221.  *
  222.  *----------------------------------------------------------------------
  223.  */
  224. char *
  225. Tcl_DbCkalloc(size, file, line)
  226.     unsigned int size;
  227.     char        *file;
  228.     int          line;
  229. {
  230.     struct mem_header *result;
  231.  
  232.     if (validate_memory)
  233.         Tcl_ValidateAllMemory (file, line);
  234.  
  235.     result = (struct mem_header *)malloc((unsigned)size + 
  236.                               sizeof(struct mem_header) + GUARD_SIZE);
  237.     if (result == NULL) {
  238.         fflush(stdout);
  239.         dump_memory_info(stderr);
  240.         panic("unable to alloc %d bytes, %s line %d", size, file, 
  241.               line);
  242.     }
  243.  
  244.     /*
  245.      * Fill in guard zones and size.  Link into allocated list.
  246.      */
  247.     result->length = size;
  248.     result->file = file;
  249.     result->line = line;
  250.     memset ((char *) result->low_guard, GUARD_VALUE, GUARD_SIZE);
  251.     memset (result->body + size, GUARD_VALUE, GUARD_SIZE);
  252.     result->flink = allocHead;
  253.     result->blink = NULL;
  254.     if (allocHead != NULL)
  255.         allocHead->blink = result;
  256.     allocHead = result;
  257.  
  258.     total_mallocs++;
  259.     if (trace_on_at_malloc && (total_mallocs >= trace_on_at_malloc)) {
  260.         (void) fflush(stdout);
  261.         fprintf(stderr, "reached malloc trace enable point (%d)\n",
  262.                 total_mallocs);
  263.         fflush(stderr);
  264.         alloc_tracing = TRUE;
  265.         trace_on_at_malloc = 0;
  266.     }
  267.  
  268.     if (alloc_tracing)
  269.         fprintf(stderr,"ckalloc %lx %d %s %d\n", result->body, size, 
  270.                 file, line);
  271.  
  272.     if (break_on_malloc && (total_mallocs >= break_on_malloc)) {
  273.         break_on_malloc = 0;
  274.         (void) fflush(stdout);
  275.         fprintf(stderr,"reached malloc break limit (%d)\n", 
  276.                 total_mallocs);
  277.         fprintf(stderr, "program will now enter C debugger\n");
  278.         (void) fflush(stderr);
  279.         kill (getpid(), SIGINT);
  280.     }
  281.  
  282.     current_malloc_packets++;
  283.     if (current_malloc_packets > maximum_malloc_packets)
  284.         maximum_malloc_packets = current_malloc_packets;
  285.     current_bytes_malloced += size;
  286.     if (current_bytes_malloced > maximum_bytes_malloced)
  287.         maximum_bytes_malloced = current_bytes_malloced;
  288.  
  289.     if (init_malloced_bodies)
  290.         memset (result->body, 0xff, (int) size);
  291.  
  292.     return result->body;
  293. }
  294.  
  295. /*
  296.  *----------------------------------------------------------------------
  297.  *
  298.  * Tcl_DbCkfree - debugging ckfree
  299.  *
  300.  *        Verify that the low and high guards are intact, and if so
  301.  *        then free the buffer else panic.
  302.  *
  303.  *        The guards are erased after being checked to catch duplicate
  304.  *        frees.
  305.  *
  306.  *        The second and third arguments are file and line, these contain
  307.  *        the filename and line number corresponding to the caller.
  308.  *        These are sent by the ckfree macro; it uses the preprocessor
  309.  *        autodefines __FILE__ and __LINE__.
  310.  *
  311.  *----------------------------------------------------------------------
  312.  */
  313.  
  314. int
  315. Tcl_DbCkfree(ptr, file, line)
  316.     char *  ptr;
  317.     char     *file;
  318.     int       line;
  319. {
  320.     struct mem_header *memp = 0;  /* Must be zero for size calc */
  321.  
  322.     /*
  323.      * Since header ptr is zero, body offset will be size
  324.      */
  325.     memp = (struct mem_header *)(((char *) ptr) - (int)memp->body);
  326.  
  327.     if (alloc_tracing)
  328.         fprintf(stderr, "ckfree %lx %ld %s %d\n", memp->body, 
  329.                 memp->length, file, line);
  330.  
  331.     if (validate_memory)
  332.         Tcl_ValidateAllMemory (file, line);
  333.  
  334.     ValidateMemory (memp, file, line, TRUE);
  335.  
  336.     total_frees++;
  337.     current_malloc_packets--;
  338.     current_bytes_malloced -= memp->length;
  339.  
  340.     /*
  341.      * Delink from allocated list
  342.      */
  343.     if (memp->flink != NULL)
  344.         memp->flink->blink = memp->blink;
  345.     if (memp->blink != NULL)
  346.         memp->blink->flink = memp->flink;
  347.     if (allocHead == memp)
  348.         allocHead = memp->flink;
  349.     free((char *) memp);
  350.     return 0;
  351. }
  352.  
  353. /*
  354.  *----------------------------------------------------------------------
  355.  *
  356.  * MemoryCmd --
  357.  *     Implements the TCL memory command:
  358.  *       memory info
  359.  *       memory display
  360.  *       break_on_malloc count
  361.  *       trace_on_at_malloc count
  362.  *       trace on|off
  363.  *       validate on|off
  364.  *
  365.  * Results:
  366.  *     Standard TCL results.
  367.  *
  368.  *----------------------------------------------------------------------
  369.  */
  370.     /* ARGSUSED */
  371. static int
  372. MemoryCmd (clientData, interp, argc, argv)
  373.     char       *clientData;
  374.     Tcl_Interp *interp;
  375.     int         argc;
  376.     char      **argv;
  377. {
  378.     char *fileName;
  379.  
  380.     if (argc < 2) {
  381.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  382.         argv[0], " option [args..]\"", (char *) NULL);
  383.     return TCL_ERROR;
  384.     }
  385.  
  386.     if (strcmp(argv[1],"trace") == 0) {
  387.         if (argc != 3) 
  388.             goto bad_suboption;
  389.         alloc_tracing = (strcmp(argv[2],"on") == 0);
  390.         return TCL_OK;
  391.     }
  392.     if (strcmp(argv[1],"init") == 0) {
  393.         if (argc != 3)
  394.             goto bad_suboption;
  395.         init_malloced_bodies = (strcmp(argv[2],"on") == 0);
  396.         return TCL_OK;
  397.     }
  398.     if (strcmp(argv[1],"validate") == 0) {
  399.         if (argc != 3)
  400.              goto bad_suboption;
  401.         validate_memory = (strcmp(argv[2],"on") == 0);
  402.         return TCL_OK;
  403.     }
  404.     if (strcmp(argv[1],"trace_on_at_malloc") == 0) {
  405.         if (argc != 3) 
  406.             goto argError;
  407.         if (Tcl_GetInt(interp, argv[2], &trace_on_at_malloc) != TCL_OK)
  408.                 return TCL_ERROR;
  409.          return TCL_OK;
  410.     }
  411.     if (strcmp(argv[1],"break_on_malloc") == 0) {
  412.         if (argc != 3) 
  413.             goto argError;
  414.         if (Tcl_GetInt(interp, argv[2], &break_on_malloc) != TCL_OK)
  415.                 return TCL_ERROR;
  416.         return TCL_OK;
  417.     }
  418.  
  419.     if (strcmp(argv[1],"info") == 0) {
  420.         dump_memory_info(stdout);
  421.         return TCL_OK;
  422.     }
  423.     if (strcmp(argv[1],"active") == 0) {
  424.         if (argc != 3) {
  425.         Tcl_AppendResult(interp, "wrong # args:  should be \"",
  426.             argv[0], " active file", (char *) NULL);
  427.         return TCL_ERROR;
  428.     }
  429.         fileName = argv [2];
  430.         if (fileName [0] == '~')
  431.             if ((fileName = Tcl_TildeSubst (interp, fileName)) == NULL)
  432.                 return TCL_ERROR;
  433.         if (Tcl_DumpActiveMemory (fileName) != TCL_OK) {
  434.         Tcl_AppendResult(interp, "error accessing ", argv[2], 
  435.             (char *) NULL);
  436.         return TCL_ERROR;
  437.     }
  438.     return TCL_OK;
  439.     }
  440.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  441.         "\":  should be info, init, active, break_on_malloc, ",
  442.         "trace_on_at_malloc, trace, or validate", (char *) NULL);
  443.     return TCL_ERROR;
  444.  
  445. argError:
  446.     Tcl_AppendResult(interp, "wrong # args:  should be \"", argv[0],
  447.         " ", argv[1], "count\"", (char *) NULL);
  448.     return TCL_ERROR;
  449.  
  450. bad_suboption:
  451.     Tcl_AppendResult(interp, "wrong # args:  should be \"", argv[0],
  452.         " ", argv[1], " on|off\"", (char *) NULL);
  453.     return TCL_ERROR;
  454. }
  455.  
  456. /*
  457.  *----------------------------------------------------------------------
  458.  *
  459.  * Tcl_InitMemory --
  460.  *     Initialize the memory command.
  461.  *
  462.  *----------------------------------------------------------------------
  463.  */
  464. void
  465. Tcl_InitMemory(interp)
  466.     Tcl_Interp *interp;
  467. {
  468. Tcl_CreateCommand (interp, "memory", MemoryCmd, (ClientData)NULL, 
  469.                   (void (*)())NULL);
  470. }
  471.  
  472. #else
  473.  
  474.  
  475. /*
  476.  *----------------------------------------------------------------------
  477.  *
  478.  * Tcl_Ckalloc --
  479.  *     Interface to malloc when TCL_MEM_DEBUG is disabled.  It does check
  480.  *     that memory was actually allocated.
  481.  *
  482.  *----------------------------------------------------------------------
  483.  */
  484. VOID *
  485. Tcl_Ckalloc (size)
  486.     unsigned int size;
  487. {
  488.         char *result;
  489.  
  490.         result = malloc(size);
  491.         if (result == NULL) 
  492.                 panic("unable to alloc %d bytes", size);
  493.         return result;
  494. }
  495.  
  496. /*
  497.  *----------------------------------------------------------------------
  498.  *
  499.  * TckCkfree --
  500.  *     Interface to free when TCL_MEM_DEBUG is disabled.  Done here rather
  501.  *     in the macro to keep some modules from being compiled with 
  502.  *     TCL_MEM_DEBUG enabled and some with it disabled.
  503.  *
  504.  *----------------------------------------------------------------------
  505.  */
  506. void
  507. Tcl_Ckfree (ptr)
  508.     VOID *ptr;
  509. {
  510.         free (ptr);
  511. }
  512.  
  513. /*
  514.  *----------------------------------------------------------------------
  515.  *
  516.  * Tcl_InitMemory --
  517.  *     Dummy initialization for memory command, which is only available 
  518.  *     if TCL_MEM_DEBUG is on.
  519.  *
  520.  *----------------------------------------------------------------------
  521.  */
  522.     /* ARGSUSED */
  523. void
  524. Tcl_InitMemory(interp)
  525.     Tcl_Interp *interp;
  526. {
  527. }
  528.  
  529. #endif
  530.